package com.cencat.common.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.cencat.common.utils.CencatStringUtils;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.Base64;
import java.util.UUID;

/**
 * 安全工具类
 * 提供加密、解密、哈希、签名等安全功能
 * 
 * @author cencat
 * @since 2024-01-01
 */
public class SecurityUtils {
    
    private static final Logger log = LoggerFactory.getLogger(SecurityUtils.class);

    // 加密算法常量
    private static final String AES_ALGORITHM = "AES";
    private static final String AES_TRANSFORMATION = "AES/ECB/PKCS5Padding";
    private static final String DES_ALGORITHM = "DES";
    private static final String RSA_ALGORITHM = "RSA";
    
    // 哈希算法常量
    private static final String MD5_ALGORITHM = "MD5";
    private static final String SHA1_ALGORITHM = "SHA-1";
    private static final String SHA256_ALGORITHM = "SHA-256";
    private static final String SHA512_ALGORITHM = "SHA-512";
    
    // HMAC算法常量
    private static final String HMAC_MD5_ALGORITHM = "HmacMD5";
    private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1";
    private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256";
    private static final String HMAC_SHA512_ALGORITHM = "HmacSHA512";
    
    // 默认密钥长度
    private static final int DEFAULT_AES_KEY_LENGTH = 128;
    private static final int DEFAULT_RSA_KEY_LENGTH = 2048;
    
    // 盐值长度
    private static final int SALT_LENGTH = 16;
    
    private SecurityUtils() {
        // 私有构造函数，防止实例化
    }
    
    /**
     * MD5哈希
     * 
     * @param data 待哈希的数据
     * @return MD5哈希值（十六进制字符串）
     */
    public static String md5(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data, MD5_ALGORITHM);
    }
    
    /**
     * MD5哈希（带盐值）
     * 
     * @param data 待哈希的数据
     * @param salt 盐值
     * @return MD5哈希值（十六进制字符串）
     */
    public static String md5WithSalt(String data, String salt) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data + salt, MD5_ALGORITHM);
    }
    
    /**
     * SHA-1哈希
     * 
     * @param data 待哈希的数据
     * @return SHA-1哈希值（十六进制字符串）
     */
    public static String sha1(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data, SHA1_ALGORITHM);
    }
    
    /**
     * SHA-256哈希
     * 
     * @param data 待哈希的数据
     * @return SHA-256哈希值（十六进制字符串）
     */
    public static String sha256(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data, SHA256_ALGORITHM);
    }
    
    /**
     * SHA-256哈希（带盐值）
     * 
     * @param data 待哈希的数据
     * @param salt 盐值
     * @return SHA-256哈希值（十六进制字符串）
     */
    public static String sha256WithSalt(String data, String salt) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data + salt, SHA256_ALGORITHM);
    }
    
    /**
     * SHA-512哈希
     * 
     * @param data 待哈希的数据
     * @return SHA-512哈希值（十六进制字符串）
     */
    public static String sha512(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return hash(data, SHA512_ALGORITHM);
    }
    
    /**
     * 通用哈希方法
     * 
     * @param data 待哈希的数据
     * @param algorithm 哈希算法
     * @return 哈希值（十六进制字符串）
     */
    private static String hash(String data, String algorithm) {
        try {
            MessageDigest digest = MessageDigest.getInstance(algorithm);
            byte[] hashBytes = digest.digest(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hashBytes);
        } catch (NoSuchAlgorithmException e) {
            log.error("哈希算法不支持: algorithm={}, error={}", algorithm, e.getMessage());
            throw new RuntimeException("哈希计算失败", e);
        }
    }
    
    /**
     * HMAC-MD5签名
     * 
     * @param data 待签名的数据
     * @param key 密钥
     * @return HMAC-MD5签名值（十六进制字符串）
     */
    public static String hmacMd5(String data, String key) {
        return hmac(data, key, HMAC_MD5_ALGORITHM);
    }
    
    /**
     * HMAC-SHA1签名
     * 
     * @param data 待签名的数据
     * @param key 密钥
     * @return HMAC-SHA1签名值（十六进制字符串）
     */
    public static String hmacSha1(String data, String key) {
        return hmac(data, key, HMAC_SHA1_ALGORITHM);
    }
    
    /**
     * HMAC-SHA256签名
     * 
     * @param data 待签名的数据
     * @param key 密钥
     * @return HMAC-SHA256签名值（十六进制字符串）
     */
    public static String hmacSha256(String data, String key) {
        return hmac(data, key, HMAC_SHA256_ALGORITHM);
    }
    
    /**
     * HMAC-SHA512签名
     * 
     * @param data 待签名的数据
     * @param key 密钥
     * @return HMAC-SHA512签名值（十六进制字符串）
     */
    public static String hmacSha512(String data, String key) {
        return hmac(data, key, HMAC_SHA512_ALGORITHM);
    }
    
    /**
     * 通用HMAC签名方法
     * 
     * @param data 待签名的数据
     * @param key 密钥
     * @param algorithm HMAC算法
     * @return HMAC签名值（十六进制字符串）
     */
    private static String hmac(String data, String key, String algorithm) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(key)) {
            return "";
        }
        
        try {
            SecretKeySpec secretKey = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), algorithm);
            Mac mac = Mac.getInstance(algorithm);
            mac.init(secretKey);
            byte[] hmacBytes = mac.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(hmacBytes);
        } catch (Exception e) {
            log.error("HMAC签名失败: algorithm={}, error={}", algorithm, e.getMessage());
            throw new RuntimeException("HMAC签名失败", e);
        }
    }
    
    /**
     * AES加密
     * 
     * @param data 待加密的数据
     * @param key 密钥（Base64编码）
     * @return 加密后的数据（Base64编码）
     */
    public static String aesEncrypt(String data, String key) {
        if (CencatStringUtils.isBlank(data) || CencatStringUtils.isBlank(key)) {
            return "";
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey);
            
            byte[] encryptedBytes = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(encryptedBytes);
        } catch (Exception e) {
            log.error("AES加密失败: error={}", e.getMessage());
            throw new RuntimeException("AES加密失败", e);
        }
    }
    
    /**
     * AES解密
     * 
     * @param encryptedData 加密的数据（Base64编码）
     * @param key 密钥（Base64编码）
     * @return 解密后的数据
     */
    public static String aesDecrypt(String encryptedData, String key) {
        if (CencatStringUtils.isBlank(encryptedData) || CencatStringUtils.isBlank(key)) {
            return "";
        }
        
        try {
            byte[] keyBytes = Base64.getDecoder().decode(key);
            SecretKeySpec secretKey = new SecretKeySpec(keyBytes, AES_ALGORITHM);
            
            Cipher cipher = Cipher.getInstance(AES_TRANSFORMATION);
            cipher.init(Cipher.DECRYPT_MODE, secretKey);
            
            byte[] encryptedBytes = Base64.getDecoder().decode(encryptedData);
            byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
            return new String(decryptedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("AES解密失败: error={}", e.getMessage());
            throw new RuntimeException("AES解密失败", e);
        }
    }
    
    /**
     * 生成AES密钥
     * 
     * @return AES密钥（Base64编码）
     */
    public static String generateAesKey() {
        return generateAesKey(DEFAULT_AES_KEY_LENGTH);
    }
    
    /**
     * 生成AES密钥
     * 
     * @param keyLength 密钥长度（位）
     * @return AES密钥（Base64编码）
     */
    public static String generateAesKey(int keyLength) {
        try {
            KeyGenerator keyGenerator = KeyGenerator.getInstance(AES_ALGORITHM);
            keyGenerator.init(keyLength);
            SecretKey secretKey = keyGenerator.generateKey();
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            log.error("生成AES密钥失败: error={}", e.getMessage());
            throw new RuntimeException("生成AES密钥失败", e);
        }
    }
    
    /**
     * 生成随机盐值
     * 
     * @return 盐值（十六进制字符串）
     */
    public static String generateSalt() {
        return generateSalt(SALT_LENGTH);
    }
    
    /**
     * 生成随机盐值
     * 
     * @param length 盐值长度（字节）
     * @return 盐值（十六进制字符串）
     */
    public static String generateSalt(int length) {
        SecureRandom random = new SecureRandom();
        byte[] salt = new byte[length];
        random.nextBytes(salt);
        return bytesToHex(salt);
    }
    
    /**
     * 生成随机字符串
     * 
     * @param length 字符串长度
     * @return 随机字符串
     */
    public static String generateRandomString(int length) {
        if (length <= 0) {
            return "";
        }
        
        String chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(chars.charAt(random.nextInt(chars.length())));
        }
        
        return sb.toString();
    }
    
    /**
     * 生成随机数字字符串
     * 
     * @param length 字符串长度
     * @return 随机数字字符串
     */
    public static String generateRandomNumber(int length) {
        if (length <= 0) {
            return "";
        }
        
        SecureRandom random = new SecureRandom();
        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            sb.append(random.nextInt(10));
        }
        
        return sb.toString();
    }
    
    /**
     * 生成UUID
     * 
     * @return UUID字符串
     */
    public static String generateUuid() {
        return UUID.randomUUID().toString();
    }
    
    /**
     * 生成UUID（去除连字符）
     * 
     * @return UUID字符串（无连字符）
     */
    public static String generateUuidWithoutHyphen() {
        return UUID.randomUUID().toString().replace("-", "");
    }
    
    /**
     * Base64编码
     * 
     * @param data 待编码的数据
     * @return Base64编码后的字符串
     */
    public static String base64Encode(String data) {
        if (CencatStringUtils.isBlank(data)) {
            return "";
        }
        return Base64.getEncoder().encodeToString(data.getBytes(StandardCharsets.UTF_8));
    }
    
    /**
     * Base64编码
     * 
     * @param data 待编码的字节数组
     * @return Base64编码后的字符串
     */
    public static String base64Encode(byte[] data) {
        if (data == null || data.length == 0) {
            return "";
        }
        return Base64.getEncoder().encodeToString(data);
    }
    
    /**
     * Base64解码
     * 
     * @param encodedData Base64编码的字符串
     * @return 解码后的字符串
     */
    public static String base64Decode(String encodedData) {
        if (CencatStringUtils.isBlank(encodedData)) {
            return "";
        }
        
        try {
            byte[] decodedBytes = Base64.getDecoder().decode(encodedData);
            return new String(decodedBytes, StandardCharsets.UTF_8);
        } catch (Exception e) {
            log.error("Base64解码失败: error={}", e.getMessage());
            return "";
        }
    }
    
    /**
     * Base64解码为字节数组
     * 
     * @param encodedData Base64编码的字符串
     * @return 解码后的字节数组
     */
    public static byte[] base64DecodeToBytes(String encodedData) {
        if (CencatStringUtils.isBlank(encodedData)) {
            return new byte[0];
        }
        
        try {
            return Base64.getDecoder().decode(encodedData);
        } catch (Exception e) {
            log.error("Base64解码失败: error={}", e.getMessage());
            return new byte[0];
        }
    }
    
    /**
     * 验证密码强度
     * 
     * @param password 密码
     * @return 密码强度等级（0-4）
     */
    public static int getPasswordStrength(String password) {
        if (CencatStringUtils.isBlank(password)) {
            return 0;
        }
        
        int strength = 0;
        
        // 长度检查
        if (password.length() >= 8) {
            strength++;
        }
        
        // 包含小写字母
        if (password.matches(".*[a-z].*")) {
            strength++;
        }
        
        // 包含大写字母
        if (password.matches(".*[A-Z].*")) {
            strength++;
        }
        
        // 包含数字
        if (password.matches(".*\\d.*")) {
            strength++;
        }
        
        // 包含特殊字符
        if (password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':,.<>?].*")) {
            strength++;
        }
        
        return Math.min(strength, 4);
    }
    
    /**
     * 检查密码是否为强密码
     * 
     * @param password 密码
     * @return 是否为强密码
     */
    public static boolean isStrongPassword(String password) {
        return getPasswordStrength(password) >= 4;
    }
    
    /**
     * 字节数组转十六进制字符串
     * 
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder result = new StringBuilder();
        for (byte b : bytes) {
            result.append(String.format("%02x", b));
        }
        return result.toString();
    }
    
    /**
     * 十六进制字符串转字节数组
     * 
     * @param hex 十六进制字符串
     * @return 字节数组
     */
    public static byte[] hexToBytes(String hex) {
        if (CencatStringUtils.isBlank(hex) || hex.length() % 2 != 0) {
            return new byte[0];
        }
        
        byte[] bytes = new byte[hex.length() / 2];
        for (int i = 0; i < hex.length(); i += 2) {
            bytes[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
        }
        return bytes;
    }
    
    /**
     * 安全常量类
     */
    public static class SecurityConstants {
        
        // 密码强度等级
        public static final int PASSWORD_STRENGTH_VERY_WEAK = 0;
        public static final int PASSWORD_STRENGTH_WEAK = 1;
        public static final int PASSWORD_STRENGTH_FAIR = 2;
        public static final int PASSWORD_STRENGTH_GOOD = 3;
        public static final int PASSWORD_STRENGTH_STRONG = 4;
        
        // 默认密钥长度
        public static final int DEFAULT_AES_128_KEY_LENGTH = 128;
        public static final int DEFAULT_AES_192_KEY_LENGTH = 192;
        public static final int DEFAULT_AES_256_KEY_LENGTH = 256;
        public static final int DEFAULT_RSA_1024_KEY_LENGTH = 1024;
        public static final int DEFAULT_RSA_2048_KEY_LENGTH = 2048;
        public static final int DEFAULT_RSA_4096_KEY_LENGTH = 4096;
        
        // 默认盐值长度
        public static final int DEFAULT_SALT_LENGTH = 16;
        public static final int DEFAULT_TOKEN_LENGTH = 32;
        
        private SecurityConstants() {
            // 私有构造函数，防止实例化
        }
    }
    
    /**
     * 密码配置类
     */
    public static class PasswordConfig {
        private int minLength = 8;
        private int maxLength = 20;
        private boolean requireLowercase = true;
        private boolean requireUppercase = true;
        private boolean requireDigit = true;
        private boolean requireSpecialChar = true;
        
        public PasswordConfig minLength(int length) {
            this.minLength = length;
            return this;
        }
        
        public PasswordConfig maxLength(int length) {
            this.maxLength = length;
            return this;
        }
        
        public PasswordConfig requireLowercase(boolean require) {
            this.requireLowercase = require;
            return this;
        }
        
        public PasswordConfig requireUppercase(boolean require) {
            this.requireUppercase = require;
            return this;
        }
        
        public PasswordConfig requireDigit(boolean require) {
            this.requireDigit = require;
            return this;
        }
        
        public PasswordConfig requireSpecialChar(boolean require) {
            this.requireSpecialChar = require;
            return this;
        }
        
        /**
         * 验证密码是否符合配置要求
         * 
         * @param password 密码
         * @return 是否符合要求
         */
        public boolean validate(String password) {
            if (CencatStringUtils.isBlank(password)) {
                return false;
            }
            
            // 长度检查
            if (password.length() < minLength || password.length() > maxLength) {
                return false;
            }
            
            // 小写字母检查
            if (requireLowercase && !password.matches(".*[a-z].*")) {
                return false;
            }
            
            // 大写字母检查
            if (requireUppercase && !password.matches(".*[A-Z].*")) {
                return false;
            }
            
            // 数字检查
            if (requireDigit && !password.matches(".*\\d.*")) {
                return false;
            }
            
            // 特殊字符检查
            if (requireSpecialChar && !password.matches(".*[!@#$%^&*()_+\\-=\\[\\]{};':,.<>?].*")) {
                return false;
            }
            
            return true;
        }
        
        // Getters
        public int getMinLength() { return minLength; }
        public int getMaxLength() { return maxLength; }
        public boolean isRequireLowercase() { return requireLowercase; }
        public boolean isRequireUppercase() { return requireUppercase; }
        public boolean isRequireDigit() { return requireDigit; }
        public boolean isRequireSpecialChar() { return requireSpecialChar; }
    }
}